
import * as effectTypes from './effectTypes'
const CANCEL_TASK = Symbol('CANCEL_TASK') // 取消 saga 生成器的标识
/**
 * 接收 saga 并运行这个 saga
 * @param {*} saga 
 */
function runSaga(env, saga, callback) {
  const runTask = {cancel: () => next(CANCEL_TASK)} // 用于取消 it 生成器函数的继续执行
  const { channel, dispatch } = env

  // saga 可能是一个生成器函数 也可能是一个迭代器对象
  let it = typeof saga === 'function' ? saga() : saga
  function next(value, isError) {
    let result;
    if(isError) {
      result = it.throw(value) // 当前迭代器抛出一个错误
    } else if (value === CANCEL_TASK) { // 取消当前 saga 生成器函数的后续执行
      result = it.return(value)
    } else {
      result = it.next(value) // 当前迭代器返回一个值
    }
    let {
      done,
      value: effect,
    } = result

    if (!done) { // !false 代表当前生成器 没有执行完毕
      if (typeof effect[Symbol.iterator] == 'function') { // 处理子saga情况
        runSaga(env, effect) // 产出的指令对象是一个迭代器对象
        next() // 当前的 saga 会继续执行
      } else if (effect instanceof Promise) {
        effect.then(next)
      } else {
        switch (effect.type) {
          case effectTypes.TAKE: { // 监听一个动作
            // take 后面没有 next 而是交给 channel.emit 来触发 next
            channel.once(effect.actionType, next) // 监听一个动作 ( 只监听一次 )
            break;
          }
          case effectTypes.PUT: { // 向仓库派发一个动作
            dispatch(effect.action)
            next()
            break;
          }
          case effectTypes.FORK: { // 开启一个子进程来运行 saga 
            const task = runSaga(env, effect.saga) // 获取 fork 返回的 task 任务对象
            next(task) // 同时继续执行当前 saga 生成器函数
            break
          }
          case effectTypes.CALL: { // 调用一个函数 promise 形式
            effect.fn(...effect.args).then( // 当 promise 成功后继续执行当前 saga 生成器函数
              next
            ) 
            break;
          }
          case effectTypes.CPS: { // 调用一个函数 回调函数形式
            effect.fn(...effect.args, (err, data) => {
              if (err) next(err, true) // 继续执行当前 saga 生成器函数
              else next(data) // 继续执行当前 saga 生成器函数
            })
            break;
          }
          case effectTypes.ALL: { // 并行执行数组中多个迭代器对象
            const { iterators } = effect
            let result = [] // 保存每个迭代器对象最后返回的结果
            let counter = 0
            iterators.forEach((iterator, index) => {
              runSaga(env, iterator, data => {
                result[index] = data // 将每个迭代器最后返回的值保存到数组对应的索引中
                if (++counter === iterators.length) { // 每个迭代器都会让 counter 递增
                  next(result) // 所有的迭代器对象都执行完毕 继续执行当前 saga 生成器
                }
              })
            })
            break;
          }
          case effectTypes.CANCEL: { // 取消子 saga 生成器函数继续执行当前 saga 生成器函数
            effect.task.cancel()
            next()
            break;
          }
          default: {
            break;
          }
        }
      }
    } else { // 当前 saga 执行完毕 ( 处理 all 情况 )
      callback && callback(effect) // 执行回调函数
    }
  }
  next()
  // 每个 runSaga 都会返回一个 runTask 对象 用于取消 it 生成器函数的继续执行
  return runTask 
}

export default runSaga
